package com.alogic.reload.impl;

import com.alogic.load.Loadable;
import com.alogic.load.Loader;
import com.alogic.reload.Reloader;
import com.alogic.xscript.Logiclet;
import com.alogic.xscript.LogicletContext;
import com.alogic.xscript.Script;
import com.alogic.xscript.doc.XsObject;
import com.alogic.xscript.doc.json.JsonObject;
import com.anysoft.util.*;
import com.anysoft.util.code.Coder;
import com.anysoft.util.code.CoderFactory;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 混合模式
 *
 * @since 1.6.13.5 [20200605 duanyy] <br>
 *
 * @version 1.6.13.31 [20210108 duanyy] <br>
 * - 加载sink对象列表时增加enable开关 <br>
 */
public class Mixed extends Reloader.Abstract {
    /**
     * a logger of log4j
     */
    protected static final Logger LOG = LoggerFactory.getLogger(Reloader.class);

    /**
     * 加载器列表
     *
     */
    protected List<Loader<Reloader>> loaders = new ArrayList<Loader<Reloader>>();

    protected Logiclet onFound = null;

    protected Coder coder = CoderFactory.newCoder("MD5");

    @Override
    public void configure(Element e, Properties p) {
        XmlElementProperties props = new XmlElementProperties(e,p);
        configure(props);

        Element onFoundElem = XmlTools.getFirstElementByPath(e, "on-found");
        if (onFoundElem != null){
            onFound = Script.create(onFoundElem, props);
        }

        NodeList nodeList = XmlTools.getNodeListByPath(e, getSinkTag());
        Factory<Loader<Reloader>> factory = new Factory<Loader<Reloader>>();
        String scope = PropertiesConstants.getString(p, "ketty.scope", "runtime");

        for (int i = 0 ;i < nodeList.getLength() ; i ++){
            Node n = nodeList.item(i);

            if (Node.ELEMENT_NODE != n.getNodeType()){
                continue;
            }

            Element elem = (Element)n;

            XmlElementProperties itemProps = new XmlElementProperties(elem,props);
            String itemScope = PropertiesConstants.getString(itemProps,"scope","",true);
            if (StringUtils.isNotEmpty(itemScope) && !itemScope.equals(scope)){
                continue;
            }

            boolean enable = PropertiesConstants.getBoolean(itemProps,"enable",true,true);
            if (!enable){
                continue;
            }

            try {
                Loader<Reloader> loader = factory.newInstance(elem, props, "module");
                if (loader != null){
                    loaders.add(loader);
                }
            }catch (Exception ex){
                LOG.error("Can not create loader from element:" + XmlTools.node2String(elem));
                LOG.error(ExceptionUtils.getStackTrace(ex));
            }
        }
    }

    protected String getSinkTag() {
        return "loader";
    }

    protected String getEncodeId(String id,String domain){
        return coder.encode(String.format("%s@%s",id,domain),"alogic",true,true);
    }

    @Override
    public boolean reload(String id, String domain,Loadable loadable) {
        String recordId = getEncodeId(id,domain);
        for (Loader<Reloader> loader:loaders){
            Reloader reloader = loader.load(recordId,true);
            if (reloader != null){
                return reloader.reload(recordId,domain,loadable);
            }
        }
        onFound(recordId,id,domain,loadable);
        return super.reload(id,domain,loadable);
    }

    protected void onFound(String recordId,String id, String domain,Loadable loadable) {
        if (onFound != null) {
            LogicletContext logicletContext = new LogicletContext(Settings.get());
            try {
                logicletContext.SetValue("$reloadId",recordId);
                logicletContext.SetValue("$reloadKey",id);
                logicletContext.SetValue("$reloadDomain",domain);
                XsObject doc = new JsonObject("root", new HashMap<String, Object>());
                onFound.execute(doc, doc, logicletContext, null);
            } catch (Exception ex) {
                LOG.info("Failed to execute onload script" + ExceptionUtils.getStackTrace(ex));
            }
        }
    }

    @Override
    public void configure(Properties p) {
        super.configure(p);
    }
}